package com.atguigu.gmall.search.service.impl;

import com.atguigu.gmall.pms.entity.BrandEntity;
import com.atguigu.gmall.pms.entity.CategoryEntity;
import com.atguigu.gmall.search.service.SearchService;
import com.atguigu.gmall.search.vo.Goods;
import com.atguigu.gmall.search.vo.SearchParamVO;
import com.atguigu.gmall.search.vo.SearchResponseAttrVo;
import com.atguigu.gmall.search.vo.SearchResponseVo;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Resource
    ElasticsearchRestTemplate restTemplate;
    @Override
    public SearchResponseVo seach(SearchParamVO searchParamVO) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        // 匹配+过滤查询的布尔组合查询构建器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1、匹配查询
        String keyword = searchParamVO.getKeyword();
//        if(StringUtils.isEmpty(keyword)){
//            //准备广告数据：创建SearchResponseVo对象响应
//            return null;
//        }
        //如果没有条件 查询所有
        if(StringUtils.isNotEmpty(keyword)){
            boolQueryBuilder.must(QueryBuilders.matchQuery("title" , keyword));
        }
        //2、过滤查询：  分类id  品牌id   检索属性+值  价格范围  是否有货
        //品牌id 分类id
        Long cid = searchParamVO.getCid();
        if(cid!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId",cid));
        }
        List<Long> brandId = searchParamVO.getBrandId();
        if(!CollectionUtils.isEmpty(brandId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",brandId));
        }
        //是否有货
        Boolean store = searchParamVO.getStore();
        if(store!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("store" , store));
        }
        //价格范围
        BigDecimal priceFrom = searchParamVO.getPriceFrom();
        BigDecimal priceTo = searchParamVO.getPriceTo();
        if(priceFrom!=null || priceTo!=null){
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");
            if(priceFrom!=null){
                rangeQueryBuilder.gte(priceFrom);
            }
            if(priceTo!=null){
                rangeQueryBuilder.lte(priceTo);
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }
        //检索属性+值
        List<String> props = searchParamVO.getProps();
        //props=5:128G-256G-512G&props=6:麒麟-高通
        if(!CollectionUtils.isEmpty(props)){
            props.forEach(prop->{
                //每个prop代表一个检索属性 和选中的值列表的字符串：5:128G-256G-512G
                //每次遍历都需要创建一个nested嵌套查询
                //在嵌套查询中 通过bool组合查询  匹配 attrId=x   并且  attrValue=值列表中任意一个的

                String[] attrIdValues = prop.split(":");
                if(attrIdValues!=null && attrIdValues.length  == 2){
                    String attrId = attrIdValues[0];
                    String[] attrValues = attrIdValues[1].split("-");
                    BoolQueryBuilder searchAttrBoolQueryBuilder = QueryBuilders.boolQuery();
                    searchAttrBoolQueryBuilder.must(QueryBuilders.termQuery("searchAttrs.attrId"
                                , attrId));
                    searchAttrBoolQueryBuilder.must(QueryBuilders.termsQuery("searchAttrs.attrValue",attrValues));
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("searchAttrs",
                            searchAttrBoolQueryBuilder,
                            ScoreMode.None));
                }
            });


        }
        builder.withQuery(boolQueryBuilder);
        //3、排序
        Integer sort = searchParamVO.getSort();
            /*
             0 ： 默认值   按照文档评分排序  _score
            1 :  价格 升序排列
            2 ：价格  降序排列
            3 ：创建时间 降序排列
            4 ： 销量 降序排列
             */
        String field;
        SortOrder sortOrder = SortOrder.DESC;
        switch (sort){
            case 1:
                field = "price";
                sortOrder = SortOrder.ASC;
                break;
            case 2:
                field = "price";
                break;
            case 3:
                field = "createTime";
                break;
            case 4:
                field = "sales";
                break;
            default:
                field = "_score";
                break;
        }
        builder.withSort(SortBuilders.fieldSort(field)
                .order(sortOrder));

        //4、分页
        Integer pageNum = searchParamVO.getPageNum();
        Integer pageSize = searchParamVO.getPageSize();
        //页码需要-1：
        builder.withPageable(PageRequest.of(pageNum-1,pageSize));
        //5、高亮
        builder.withHighlightBuilder(new HighlightBuilder()
            .field("title").preTags("<font style='color:red;'>")
            .postTags("</font>"));
        //6、聚合
        //6.1 分类聚合
        builder.addAggregation(AggregationBuilders.terms("categoryIdAgg")
            .field("categoryId")
            .subAggregation(AggregationBuilders.terms("categoryNameAgg")
                .field("categoryName")));
        //6.2 品牌聚合
        builder.addAggregation(AggregationBuilders.terms("brandIdAgg")
            .field("brandId")
            .subAggregation(AggregationBuilders.terms("brandNameAgg")
                .field("brandName"))
            .subAggregation(AggregationBuilders.terms("logoAgg")
                .field("logo")));
        //6.3 检索属性+值 聚合
        builder.addAggregation(AggregationBuilders.nested("searchAttrNestedAgg","searchAttrs")
            .subAggregation(AggregationBuilders.terms("searchAttrIdAgg")
                .field("searchAttrs.attrId")
                    .subAggregation(AggregationBuilders.terms("searchAttrNameAgg")
                        .field("searchAttrs.attrName")
                    )
                    .subAggregation(AggregationBuilders.terms("searchAttrValueAgg")
                        .field("searchAttrs.attrValue")
                    )
            )
        );
        SearchHits<Goods> searchHits = restTemplate.search(builder.build(), Goods.class, IndexCoordinates.of("goods"));
        //解析查询结果： 将SearchResponseVo需要的属性值解析出绑定 展示
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        searchResponseVo.setPageNum(pageNum);
        searchResponseVo.setPageSize(pageSize);
        searchResponseVo.setTotal(searchHits.getTotalHits());
        List<Goods> goodsList = searchHits.get().map(searchHit->{
            Goods goods = searchHit.getContent();
            //解析高亮标题
            List<String> list = searchHit.getHighlightField("title");
            if(!CollectionUtils.isEmpty(list)){
                goods.setTitle(StringUtils.join(list , ""));
            }
            return goods;
        }).collect(Collectors.toList());
        searchResponseVo.setGoodsList(goodsList);

        //解析聚合结果：
        Map<String, Aggregation> map = searchHits.getAggregations().asMap();
        //品牌桶数据
        ParsedLongTerms brandIdAgg = (ParsedLongTerms) map.get("brandIdAgg");
        List<? extends Terms.Bucket> buckets = brandIdAgg.getBuckets();
        List<BrandEntity> brands = buckets.stream().map(bucket->{
            BrandEntity brandEntity = new BrandEntity();
            String bId = ((Terms.Bucket) bucket).getKey().toString();
            brandEntity.setId(Long.parseLong(bId));
            ParsedStringTerms brandNameAgg = (ParsedStringTerms) ((Terms.Bucket) bucket).getAggregations().asMap().get("brandNameAgg");
            ParsedStringTerms logoAgg = (ParsedStringTerms) ((Terms.Bucket) bucket).getAggregations().asMap().get("logoAgg");
            String brandName = brandNameAgg.getBuckets().get(0).getKey().toString();
            String logo = logoAgg.getBuckets().get(0).getKey().toString();
            brandEntity.setName(brandName);
            brandEntity.setLogo(logo);
            return  brandEntity;
        }).collect(Collectors.toList());
        searchResponseVo.setBrands(brands);

        //分类桶数据
        ParsedLongTerms categoryIdAgg = (ParsedLongTerms) map.get("categoryIdAgg");
        List<CategoryEntity> categories = categoryIdAgg.getBuckets().stream().map(bucket->{
            CategoryEntity categoryEntity = new CategoryEntity();
            String categoryId = ((Terms.Bucket) bucket).getKey().toString();
            categoryEntity.setId(Long.parseLong(categoryId));
            ParsedStringTerms categoryNameAgg = (ParsedStringTerms) ((Terms.Bucket) bucket).getAggregations().asMap().get("categoryNameAgg");
            String categoryName = categoryNameAgg.getBuckets().get(0).getKey().toString();
            categoryEntity.setName(categoryName);
            return categoryEntity;
        }).collect(Collectors.toList());
        searchResponseVo.setCategories(categories);

        //检索属性+值桶数据
        ParsedNested searchAttrNestedAgg = (ParsedNested) map.get("searchAttrNestedAgg");
        ParsedLongTerms searchAttrIdAgg = (ParsedLongTerms) searchAttrNestedAgg.getAggregations().asMap().get("searchAttrIdAgg");
        //检索属性id 的桶
        List<SearchResponseAttrVo> filters = searchAttrIdAgg.getBuckets().stream().map(bucket->{
            //每个检索属性id 映射一个 检索属性
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            String searchAttrId = ((Terms.Bucket) bucket).getKey().toString();
            searchResponseAttrVo.setAttrId(Long.parseLong(searchAttrId));
            //一个检索属性id 只有一个检索属性名
            ParsedStringTerms searchAttrNameAgg = (ParsedStringTerms) ((Terms.Bucket) bucket).getAggregations().asMap().get("searchAttrNameAgg");
            String searchAttrName = searchAttrNameAgg.getBuckets().get(0).getKeyAsString();
            searchResponseAttrVo.setAttrName(searchAttrName);
            //一个检索属性id  有多个检索属性值
            ParsedStringTerms searchAttrValueAgg = (ParsedStringTerms) ((Terms.Bucket) bucket).getAggregations().asMap().get("searchAttrValueAgg");
            List<String> attrValues = searchAttrValueAgg.getBuckets().stream().map(attrValueBucket->{
                String searchAttrValue = ((Terms.Bucket) attrValueBucket).getKey().toString();
                return searchAttrValue;
            }).collect(Collectors.toList());
            searchResponseAttrVo.setAttrValues(attrValues);
            return searchResponseAttrVo;
        }).collect(Collectors.toList());

        searchResponseVo.setFilters(filters);

//        System.out.println(brandIdAgg.getClass().getName());
//        System.out.println(categoryIdAgg.getClass().getName());
//        System.out.println(searchAttrNestedAgg.getClass().getName());
        return searchResponseVo;
    }
}
